#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "sim_process.h"
#include "sim_main_process.h"
#include <errno.h>


VSIM_Auth_t VSIM_Auth[VSIM_AUTH_RECORD_MAX];
extern Common_Data Data;

static int VSIM_FILE_SEARCH(VUSIM_STA *sta)
{
	int i = 0;
	int Fcb_NUM;
	FILE_CONTROL_BLOCK *Temp_FCB;
	int Fcp_pointer = 0;
	int Fct_pointer = 0;
	int status = 0;
	int DF_Type = 0;//modify by ywy 20150519
	
	Fcb_NUM = Data.File_FCB[0];
	Temp_FCB = (FILE_CONTROL_BLOCK *)(Data.File_FCB + 1);
	
	do{
		if((0==memcmp(sta->code,Temp_FCB->code,sta->code_len))
			&&((sta->code_len)==(Temp_FCB->code_len)))
		{
			DBUG("simctrl:file %s found\n",Temp_FCB->comments);
			break;
		}
		Temp_FCB++;
		i++;
	}while(i<Fcb_NUM);

	Fcp_pointer=(int)(Temp_FCB->FCP_pointerH);
	Fcp_pointer=(Fcp_pointer<<8);
	Fcp_pointer|=(int)(Temp_FCB->FCP_pointerL);

	Fct_pointer=(int)(Temp_FCB->content_pointerH);
	Fct_pointer=(Fct_pointer<<8);
	Fct_pointer|=(int)(Temp_FCB->content_pointerL);

	DF_Type =(int)(Temp_FCB->typeH);
	DF_Type =(DF_Type<<8);
	DF_Type |=(int)(Temp_FCB->typeL);

	if(i>=Fcb_NUM)
	{
		sta->FCP_point = 0;
		sta->FCP_len   = 0;
		sta->FCT_point = 0;
		sta->FCT_len   = 0;
		sta->SW[0]     = FILE_STA;
		sta->SW[1]     = 0x04;
		status = 1;
	}
	else
	{
		sta->FCP_point = Fcp_pointer;
		sta->FCP_len   = (int)(Temp_FCB->FCP_len);
		sta->FCT_point = Fct_pointer;
		sta->FCT_len   = Temp_FCB->content_len;
		sta->SW[0]     = OK_WITH_LE;
		sta->SW[1]     = Temp_FCB->FCP_len;
		sta->Current_DFType = DF_Type;
	}

	return status;
}

static int VSIM_FCP_search(VUSIM_STA *sta, u8 *buf)
{
	unsigned char *TXBuf = Data.File_FCP;

	if(0==sta->FCP_len)
		return 1;
	
	TXBuf += (sta->FCP_point);
	memcpy(buf, TXBuf, sta->FCP_len);
	
	return 0;
}

static int VSIM_FCT_search(VUSIM_STA *sta,unsigned char *buf)
{
	unsigned char *TXBuf = Data.File_FCT;
	if(0 == sta->FCT_len)
		return -1;

	TXBuf+=(sta->FCT_point);
	memcpy(buf,TXBuf,sta->FCT_len);

	return 0;
}

static void VSIM_APDU_SELECTFILE2(unsigned char* RecvBuf, int RecvLen, APDU_HEAD *head, VUSIM_STA *VS)
{
	int status = 0;
	unsigned char Buf[128] = {0};

	status = VSIM_APDU_Para_Test(RecvBuf, RecvLen, head, 500);
	if(status < 0)
		return;

	memcpy(Buf, (unsigned char *)(head), 5);
	memcpy(Buf+5, RecvBuf, head->LcLe);
	//Sim_Log_Hex("C=",Buf, 5+head->LcLe);

	switch(RecvBuf[0])
	{
		case 0x3F:
			memcpy(VS->code,RecvBuf,2);
			VS->code_len = 2;
			break;
		case 0x7F:
		case 0x2F:
			memcpy(2+VS->code,RecvBuf,2);
			VS->code[0] = 0x3F;
			VS->code[1] = 0x00;
			VS->code_len = 4;
			break;
		case 0x6F:
			memcpy(4+VS->code,RecvBuf,2);
			VS->code_len = 6;
			break;
		default:
			DBUG("[sim-select] Dir Recieve is wrong%02x %02x\n",RecvBuf[0],RecvBuf[1]);
			break;
	}

	if(VSIM_FILE_SEARCH(VS))
	{
		DBUG("vsim file not found!\n");
		VS->SW[0] = FILE_STA;
		VS->SW[1] = 0x04;
	}

	//Sim_Log_Hex("R=", VS->SW, 2);
	VUSIM_RetData(VS->SW, 2);
	(VS->APDU_STAGE) = APDU_STAGE0;

	return ;	
}

static void VSIM_APDU_GETRESPONSE(APDU_HEAD * head ,VUSIM_STA * VS)
{
	int status = 0;
	//Sim_Log_Hex("C=",(unsigned char *)head, 5);

	if( VS->Authenticate_Flag ) //authenticate response
	{
		VS->Authenticate_Flag = 0;//FALSE;
		memcpy(VS->TXbuf, Data.g_TT_ctl.RcvBuf, Data.g_TT_ctl.Rcv_Len);
		VS->TX_len = Data.g_TT_ctl.Rcv_Len;
	}	
	else
	{
		//VUSIM_ReProcessByte(head->INS);
		status = VSIM_FCP_search(VS, VS->TXbuf);
		VS->TX_len = (2 + head->LcLe);
		if(VS->USAT_Flag)
		{
			VS->SW[0] = Data.USAT_SW1;			
			VS->SW[1] = Data.USAT_SW2;
		}
		else{
			VS->SW[0] = OK_WITHOUT_LE;			
			VS->SW[1] = 0x00;
		}
		memcpy(((head->LcLe)+VS->TXbuf),VS->SW,2);	
	}
	
	VS->APDU_STAGE = APDU_STAGE0;
	//Sim_Log_Hex("R=", VS->TXbuf, VS->TX_len);
	VUSIM_RetData(VS->TXbuf,VS->TX_len);

	return ;
}

static void VSIM_APDU_READ(APDU_HEAD * head ,VUSIM_STA * VS)
{
	//Sim_Log_Hex("C=",(unsigned char *)head,5);
	if( READ_RECORD == head->INS )
	{
		/* the pointer of FCT = p1 x lenth of every record*/
		if(0 == VSIM_FILE_SEARCH(VS))//file content found
			VS->FCT_point += ((int)(((head->P1)-1)*(head->LcLe)));	
	}

	if(VSIM_FCT_search(VS, VS->TXbuf))
	{
		VS->SW[0] = FILE_STA;			
		VS->SW[1] = 0x02;
		VS->TX_len = 0;
	}
	else 
	{
		if(VS->USAT_Flag)
		{
			VS->SW[0] = Data.USAT_SW1;			
			VS->SW[1] = Data.USAT_SW2;	
		}
		else
		{
			VS->SW[0] = SW1_OK_WITHOUT_Le;			
			VS->SW[1] = SW2_OK_WITHOUT_Le;
		}
		//VUSIM_ReProcessByte(head->INS);
		VS->TX_len = head->LcLe;
	}
	
	memcpy(((VS->TX_len) + VS->TXbuf), VS->SW, 2 );
	//Sim_Log_Hex("R=", VS->TXbuf, 2+VS->TX_len);
	VUSIM_RetData(VS->TXbuf, 2 + VS->TX_len);        //20150106 for Thiland test	
	VS->APDU_STAGE = APDU_STAGE0;
	
	return ;		
}

static void VSIM_APDU_VERIFY(APDU_HEAD * head , VUSIM_STA * VS)
{
	//Sim_Log_Hex("C=", (unsigned char *)head, 5);
	VS->SW[0] = 0x63;
	VS->SW[1] = 0xC3;
	//Sim_Log_Hex("R=", VS->SW, 2);
	VUSIM_RetData(VS->SW, 2);
	VS->APDU_STAGE = APDU_STAGE0;

	return ;	
}

static void VSIM_APDU_UNBLOCK(APDU_HEAD *head, VUSIM_STA * VS)
{
	//Sim_Log_Hex("C=",(unsigned char *)head, 5);
	VS->SW[0]=0x63;
	VS->SW[1]=0xCA;
	VS->APDU_STAGE = APDU_STAGE0;
	//Sim_Log_Hex("R=",VS->SW, 2);
	VUSIM_RetData(VS->SW, 2);

	return ;	
}

static void VSIM_APDU_TERMINAL_PROFILE2(unsigned char* RecvBuf, int RecvLen, APDU_HEAD * head ,VUSIM_STA * VS)
{
	int status =0;
	unsigned char Buf[256] ={0};

	status = VSIM_APDU_Para_Test(RecvBuf, RecvLen, head, 500);
	if(status < 0)
		return;

	memcpy(Buf, (unsigned char *)head, 5);
	memcpy(5+Buf, RecvBuf, head->LcLe);
	//Sim_Log_Hex("C=",Buf,5+head->LcLe);
	VS->SW[0] = Data.USAT_SW1;
	VS->SW[1] = Data.USAT_SW2;
	//Sim_Log_Hex("R=",VS->SW,2);
	VUSIM_RetData(VS->SW,2);
	VS->APDU_STAGE = APDU_STAGE0;

	return ;
}

static void VSIM_APDU_STATUS(APDU_HEAD * head, VUSIM_STA * VS)
{
	int status = 0;
	unsigned char * p_tx;
	
	p_tx = VS->TXbuf;
	switch(VS->code_len)
	{
		case 2:
		case 4:
			VS->code_len = 2;
			break;
		case 6:
			VS->code_len = 4;
			break;
		default:
			break;
	}
	//VUSIM_ReProcessByte(head->INS);
	
	VSIM_FILE_SEARCH(VS);
	VSIM_FCP_search(VS,p_tx);
	if(VS->USAT_Flag)
	{
		*(p_tx + head->LcLe) = Data.USAT_SW1;
		*(p_tx + 1+ head->LcLe) = Data.USAT_SW2;
	}
	else
	{
		*(p_tx + head->LcLe) = SW1_OK_WITHOUT_Le;
		*(p_tx + 1+ head->LcLe) = SW2_OK_WITHOUT_Le;
	}
	
	VS->TX_len= 2+head->LcLe;
	VUSIM_RetData(VS->TXbuf, VS->TX_len);
	VS->APDU_STAGE = APDU_STAGE0;
	return ;
}

static void VSIM_APDU_FETCH(APDU_HEAD * head ,VUSIM_STA * VS)
{
	//Sim_Log_Hex("C=",(unsigned char *)head,5);
	unsigned char * p_tx = VS->TXbuf;
	VS->USAT_Flag = 0;//FALSE;

	if((Data.FCH_Size) != head->LcLe)
	{
		*(p_tx++)=0x6F;
		*(p_tx++)=0x00;
		VS->TX_len = 0;
	}
	else
	{
		memcpy(p_tx, Data.File_FCH, Data.FCH_Size);
		p_tx += Data.FCH_Size ;
		VS->TX_len =  Data.FCH_Size;
		*(p_tx++)=SW1_OK_WITHOUT_Le;
		*(p_tx++)=SW2_OK_WITHOUT_Le;
		//VUSIM_ReProcessByte(head->INS);
	}

	//Sim_Log_Hex("R=",VS->TXbuf,2+VS->TX_len);
	VUSIM_RetData(VS->TXbuf, 2+VS->TX_len);
	VS->APDU_STAGE = APDU_STAGE0;

	return ;
}

static void VSIM_APDU_TRESPONSE2(unsigned char* RecvBuf, int RecvLen, APDU_HEAD * head, VUSIM_STA * VS)
{
	int status =0;
	unsigned char Buf[256]={0};

	status = VSIM_APDU_Para_Test(RecvBuf, RecvLen, head, 500);
	if(status < 0)
		return;

	memcpy(Buf, (unsigned char *)head, 5);
	memcpy(5 + Buf, RecvBuf, head->LcLe);
	//Sim_Log_Hex("C=", Buf,5 + head->LcLe);
	if(VS->USAT_Flag)
	{
		VS->SW[0] = Data.USAT_SW1;			
		VS->SW[1] = Data.USAT_SW2;
	}
	else
	{
		VS->SW[0] = SW1_OK_WITHOUT_Le;			
		VS->SW[1] = SW2_OK_WITHOUT_Le;
	}

	//Sim_Log_Hex("R=",VS->SW,2);
	VUSIM_RetData(VS->SW,2);
	VS->APDU_STAGE = APDU_STAGE0;

	return ;	
}

static void RandInsert(unsigned char * Rand, unsigned char cnt)
{
	VSIM_Auth_t * p_vsim_auth = (VSIM_Auth_t *)(&(VSIM_Auth[Data.Point_Vsim_Auth].cnt));
	struct timeval cur_time;

	gettimeofday(&cur_time, NULL);
	memcpy(p_vsim_auth->AuthRand, Rand, 16);
	p_vsim_auth->cnt = cnt;
	p_vsim_auth->AuthResult_Len = 0;
	memcpy(&(p_vsim_auth->snd_time), &cur_time, sizeof(cur_time)); 
	Data.Point_Vsim_Auth++;

	if(Data.Point_Vsim_Auth >= VSIM_AUTH_RECORD_MAX)
		Data.Point_Vsim_Auth = 0;
	
	return;
}

static int SearchRand(unsigned char *Rand)
{
    int i = 0;
    if(Rand == NULL) return VSIM_AUTH_RECORD_MAX;

	for(i = 0; i < VSIM_AUTH_RECORD_MAX; i++)
	{
		if(!memcmp(VSIM_Auth[i].AuthRand, Rand, 16))
		{
			break;
		}
	}

	return i;
}

static int VSIM_Auth_Data_Recv(unsigned char  *Rand, unsigned char *Buf)
{
    int i = 0;
	int num = 0;
    int retVal = 0;
	int	misc = 0;
    VSIM_Auth_t *p_Result = NULL;
    //Debuf_Print_Data(Rand, 16, "random recv from simuart is");

	num = SearchRand(Rand);
	if(num >= VSIM_AUTH_RECORD_MAX)
    {
        DBUG("[VSIM_Auth_Data_Recv]:TT recv technology error random is not in Rand-Result list!\n");
        return 0;
    }

    p_Result = &VSIM_Auth[num];

    do{
        if(TT_Recved == p_Result->flag )
        {
            Data.g_TT_Success_time = time(NULL);
            memcpy(Buf, p_Result->AuthResult, 15 );
            retVal = p_Result->AuthResult_Len;
            p_Result->flag = TT_Idel;

			misc = (p_Result->rcv_time.tv_sec - p_Result->snd_time.tv_sec)*1000+(p_Result->rcv_time.tv_usec - p_Result->snd_time.tv_usec)/1000;
    		DBUG( "TT success!!! time spend on socket is %ld ms, cnt=%d, the i is %d, the num is %d\n", misc, p_Result->cnt, i, num);

            break;
        }
        i++;
        Delay(0, 5);
    }while(i < 300);

    if(i >= 300)
    {
        DBUG("[VSIM_Auth_Data_Recv]:[Cnt%d Fail!!!]network time out!!!\n", p_Result->cnt);
    }
    DBUG("TTTTTTTTTTT cnt=%d: %d bytes snd to MiFi TTTTTTTTTTT\n", p_Result->cnt, retVal);

    return retVal;
}

static void VSIM_Auth_Data_Snd(unsigned char *Rand, unsigned char *Head)
{
    pid_t pid = getpid();
    int status = 0;
	int num = -1;
    unsigned char Buf[256] = {0};
    TT_Ctl_t *p_ctl = &Data.g_TT_ctl ;
    int i = 0;
	struct timeval cur_time;

	num = SearchRand(Rand);
	DBUG( "This come from %s, the Snd_Cnt is %d\n", __func__, p_ctl->Snd_Cnt);
	if(num >= VSIM_AUTH_RECORD_MAX)
	{
        status = TT_Dat_Pack1(Rand, 16, Head, Buf) ;
        p_ctl->Snd_Cnt ++;
        p_ctl->Snd_Len = TT_Dat_Pack2(Buf, status, p_ctl->Snd_Cnt, p_ctl->SndBuf);
        RandInsert(Rand, p_ctl->Snd_Cnt);
        DBUG( "TT start now cnt=%d\n",p_ctl->Snd_Cnt);
        //kill(pid, SIGRTMIN); //notice socket to send tt data
    }
    else
    {
		gettimeofday(&cur_time, NULL);//add by 20160823
		memcpy(&(VSIM_Auth[num].snd_time), &cur_time, sizeof(struct timeval));//add by 20160823

		if(TT_Snd_Err == VSIM_Auth[num].flag)
		{
        	DBUG("last tt send error, retry again\n");
        	//kill(pid, SIGRTMIN); //notice socket to send tt data
		}
    }

    return;
}

static void VSIM_APDU_AUTHENTICATE2(unsigned char* RecvBuf, int RecvLen, APDU_HEAD * head ,VUSIM_STA * VS)
{
	int status =0;
	int recv_cnt = 0;
	int recv_sta = 0;
	unsigned char Buf[256]={0};
	const unsigned char AuthSuccess[2] = {0x9F, 0x0C};//the length must be 0x0c
	const unsigned char AuthError[2] = {0x94, 0x08};
	
	status = VSIM_APDU_Para_Test(RecvBuf, RecvLen, head, 50);
	if(status < 0)
		return;

	memcpy(Buf, (unsigned char *)head, 5);
	memcpy(5+Buf, RecvBuf, head->LcLe);
	//Sim_Log_Hex("C=",Buf,5+head->LcLe);
	VSIM_Auth_Data_Snd(RecvBuf, (unsigned char*)head);
	
	Data.g_TT_ctl.Rcv_Len = VSIM_Auth_Data_Recv(RecvBuf, Data.g_TT_ctl.RcvBuf);
	
	if(3 < Data.g_TT_ctl.Rcv_Len)
	{
		VS->Authenticate_Flag = 1;//TRUE;
		memcpy(VS->TXbuf, AuthSuccess, 2);
		VS->TX_len = 2;
	}
	else
	{
		VS->APDU_STAGE = APDU_STAGE0;
		return ;
	}

	VS->APDU_STAGE = APDU_STAGE0;
	//Sim_Log_Hex("R=",VS->TXbuf,VS->TX_len);
	VUSIM_RetData(VS->TXbuf, VS->TX_len);
	return ;	
}

static void VSIM_APDU_UPDATE2(unsigned char* RecvBuf, int RecvLen, APDU_HEAD * head ,VUSIM_STA * VS)
{
	int status = 0;
	unsigned char Buf[256]={0};
	unsigned char EF_PLMN[6] = {0x3F,0x00,0x7F,0x20,0x6F,0x7B};

	status = VSIM_APDU_Para_Test(RecvBuf, RecvLen, head, 500);
	if(status < 0)
		return;

	memcpy(Buf, (unsigned char *)head, 5);
	memcpy(5+Buf, RecvBuf, head->LcLe);
	//Sim_Log_Hex("C=",Buf,5+head->LcLe);

	if(memcmp(EF_PLMN, VS->code, 6))//6f 7b do not update FPLMN 2016.02.25
	{
		VUSIM_FCT_UPDATE(VS, RecvBuf, head->LcLe);		
	}
	else
	{
		DBUG("do not update sim fplmn\n");
	}

	if(VS->USAT_Flag)
	{
		VS->SW[0] = Data.USAT_SW1;			
		VS->SW[1] = Data.USAT_SW2;
	}
	else
	{
		VS->SW[0] = SW1_OK_WITHOUT_Le;			
		VS->SW[1] = SW2_OK_WITHOUT_Le;
	}

	//Sim_Log_Hex("R=",VS->SW,2);
	VUSIM_RetData(VS->SW,2);
	VS->APDU_STAGE = APDU_STAGE0;

	return ;
}

static void VSIM_APDU_PROC1(APDU_HEAD * head ,VUSIM_STA * VS)
{
	switch(head->INS)
	{
		case SELECT_FILE:
			APDU_Send_Byte_Ins(head, VS);
			break;
		case GET_RESPONSE:
			VSIM_APDU_GETRESPONSE(head,VS);
			break;
		case READ_BINARY:
		case READ_RECORD:
			VSIM_APDU_READ(head,VS);
			break;
		case VERIFY:
			VSIM_APDU_VERIFY(head,VS);
			break;
		case UNBLOCK_PIN:
			VSIM_APDU_UNBLOCK(head,VS);
			break;
		case TERMINAL_PROFILE:
			VS->USAT_Flag=1;//TRUE;
			APDU_Send_Byte_Ins(head, VS);
			break;		
		case STATUS:
			VSIM_APDU_STATUS(head,VS);
			break;
		case FETCH:
			VSIM_APDU_FETCH(head,VS);
			break;
		case TERMINAL_RESPONSE:
		case INTERNAL_AUTHENTICATE:	
		case UPDATE_BINARY:
		case UPDATE_RECORD:
			APDU_Send_Byte_Ins(head, VS);
			break;
			break;
		default:
			break;
	}

	return;
}

static void VSIM_APDU_PROC2(unsigned char *RcvBuf, int iRecvLen, APDU_HEAD * head ,VUSIM_STA * VS)
{
	switch(head->INS)
	{
		case SELECT_FILE:
			VSIM_APDU_SELECTFILE2(RcvBuf, iRecvLen, head,VS);
			break;
		case TERMINAL_PROFILE:
			VSIM_APDU_TERMINAL_PROFILE2(RcvBuf, iRecvLen, head,VS);
			break;		
		case TERMINAL_RESPONSE:
			VSIM_APDU_TRESPONSE2(RcvBuf, iRecvLen, head,VS);
			break;
		case INTERNAL_AUTHENTICATE: 
			VSIM_APDU_AUTHENTICATE2(RcvBuf, iRecvLen, head,VS);
			break;
		case UPDATE_BINARY:
		case UPDATE_RECORD:
			VSIM_APDU_UPDATE2(RcvBuf, iRecvLen, head,VS);
			break;
		default:
			break;
	}

	return ;
}

void VSIM_MAIN_PROC(unsigned char *Buf, int iLen)
{
	int status = 0;
	APDU_HEAD *p_head = &Data.APDUhead;
	VUSIM_STA *vs = &Data.VUSIM_Global;
	
	switch(vs->APDU_STAGE)
	{
		case APDU_STAGE0:
			status = VUSIM_APDU_HeadDet(Buf, iLen);
			if (status <= 0) break;
			memcpy((unsigned char*)(p_head), Buf, 5);
			VSIM_APDU_PROC1(p_head, vs);
			break;
		case APDU_STAGE1:
			VSIM_APDU_PROC2(Buf, iLen, p_head, vs);
			break;
		default:
			DBUG("[VSIM_MAIN_PROC]ERROR DATA APDU_STAGE:%d\n",vs->APDU_STAGE);//YYH TEST
			break;
	}

	return ;
}
